'''
Created on 12.12.2010

@author: imagovrn
'''

from pygraph.classes.graph import graph
import random

class Solution(object):
    '''
    classdocs
    '''

    def __init__(self, graph, nodes):
        '''
        Constructor
        '''
        assert isinstance(nodes, set)
        
        self._graph = graph
        self._nodes = nodes
        self._inner = None
        self._outer = None
                
        estimate = self._estimate(self._graph, self._nodes)
        
        self._inner = estimate["inner"]
        self._outer = estimate["outer"]
        
    def __repr__(self):
        return self._nodes.__str__()
    
    def crossover(self, solution):
        assert len(self._nodes) == len(solution.nodes)
        assert self._graph == solution.graph
        
        cross_gene = int(random.uniform(0, len(self._nodes)))
        
        first_child_nodes = set()
        second_child_nodes = set()
        
        first_parent_nodes = list(self._nodes)
        second_parent_nodes = list(solution.nodes)
         
        for i in xrange(len(self._nodes)):
            if i != cross_gene:
                first_child_nodes.add(first_parent_nodes[i])
                second_child_nodes.add(second_parent_nodes[i])
            else:
                second_child_nodes.add(first_parent_nodes[i])
                first_child_nodes.add(second_parent_nodes[i])
        
        if len(first_child_nodes) != len(self._nodes) or len(second_child_nodes) != len(self._nodes):
            first_child_nodes = set(first_parent_nodes)
            second_child_nodes = set(second_parent_nodes)
            
        return [Solution(self._graph, first_child_nodes),
                Solution(self._graph, second_child_nodes)]
    
    @classmethod        
    def from_nodelist(cls, graph, nodelist):
        """
        create solution from graph and nodelist
        """
        nodeset = set(nodelist)
        assert len(nodeset) == len(nodelist), "Similar nodes found"
        return Solution(graph, nodeset)
    
    @classmethod
    def from_nodelists(cls, graph, nodelists):
        """
        create solution list from nodelists
        """
        result = list()
        for nodelist in nodelists:
            result.append(cls.from_nodelist(graph, nodelist))
        return result
        
    def _estimate(self, graph, nodes):
        """
        inner >= outer
        """
        assert isinstance(nodes, set)
        
        inner_visited = set()
        inner_weight = 0
        outer_weight = 0
        
        for start_node in nodes:
            neighbors = set(graph.neighbors(start_node))
            inner = neighbors.intersection(nodes) - inner_visited 
            outer = neighbors - nodes
            
            for end_node in inner:
                inner_weight += graph.edge_weight((start_node, end_node))
            inner_visited.add(start_node)
            
            for end_node in outer:
                outer_weight += graph.edge_weight((start_node, end_node))
                
        return {"inner":inner_weight, "outer":outer_weight}
        
    @property
    def nodes(self):
        """
        Return nodes
        """
        return self._nodes
    
    @property
    def inner(self):
        """
        Return nodes
        """
        return self._inner
    
    @property
    def outer(self):
        """
        Return nodes
        """
        return self._outer
    
    @property
    def graph(self):
        """
        Return nodes
        """
        return self._graph
    
    def estimate_string(self):
        """
        get estimate as string
        """
        return "{inner} >= {outer}".format(inner=self._inner, outer=self._outer)
    
    def target_value(self):
        """
        get target value from estimates
        """
        return self._outer - self._inner